import http, {IncomingHttpHeaders} from 'http'
import https from 'https'
import path from 'path'
import querystring from 'querystring'
import Matcher from './matcher'

interface CreateListenerParams {
  onData?: (chunk: any) => void
  onError?: (msg: string) => void
  onEnd?: (url: string) => void
  onStart?: (headers: IncomingHttpHeaders) => void
  redirect?: boolean
}

export function createListener({onData, onError, onEnd, onStart, redirect} = {} as CreateListenerParams) {
  return function start(params = {} as any) {
    const {data, url, ...options} = params
    const client = url.startsWith('https://') ? https : http
    let body = typeof data === "object" && Object.keys(data).length ? querystring.stringify(data) : ''
    const req = client.request(url, {
      ...options,
      headers: {
        referer: url,
        ...(body ? {
          'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
          accept: 'application/json, text/javascript, */*; q=0.01',
          'accept-language': 'zh-CN,zh;q=0.9',
        } : {}),
        ...options.headers,
      }
    }, res => {
      if (res.headers.location) {
        start({
          url: res.headers.location,
          headers: {
            referer: url,
          }
        })
      } else {
        const {type, charset} = parseContentType(res.headers["content-type"])
        if (charset) res.setEncoding(charset as BufferEncoding)

        let buf = '';
        if (type === 'text/html') {
          res.on("data", chunk => (buf += chunk))
          res.on("end", () => {
            // 解析 html
            const {iframe, ajaxdata} = new Matcher(buf).matchIframe().matchVar('ajaxdata').done()
            if (iframe) {
              const reqOption = {
                url: joinUrl(url, iframe, true)
              }
              start(reqOption)
            } else if (ajaxdata) {
              const value = new Matcher(buf).matchObject('type').matchObject('url').done()
              const reqOption = {
                url: joinUrl(url, value.url),
                data: {
                  action: 'downprocess',
                  sign: ajaxdata,
                  ves: 1,
                },
                method: value.type,
                headers: {
                  referer: url,
                }
              }
              start(reqOption)
            } else /*if (isFinalDownload)*/ {
              const value = new Matcher(buf).matchSecondVerify().matchObject('type').matchObject('url').done()
              const reqOption = {
                url: joinUrl(url, value.url, true),
                method: value.type,
                data: value.data,
                headers: {
                  referer: url,
                },
              }
              start(reqOption)
            }
          })
        } else if (type === 'text/json') {
          res.on("data", chunk => (buf += chunk))
          res.on("end", () => {
            // 1. 预下载，2.最终下载(application/octet-stream)
            const json = JSON.parse(buf)
            const reqOption = {
              url: `${json.dom}/file/${json.url}`,
              headers: {
                referer: options.headers.referer,
              }
            }
            if (redirect) {
              onEnd?.(reqOption.url)
            } else {
              start(reqOption)
            }
          })
        } else if (type === 'application/octet-stream') {
          // 下载
          onStart?.(res.headers)
          res.on("data", chunk => onData?.(chunk))
          res.on("end", () => onEnd?.(url))
        } else {
          onError?.(`找不到 type 类型：${type}`)
        }
        res.on("error", err => onError?.(err.message))
      }
    })
    req.write(body)
    req.end()
  }
}

function parseContentType(contentType = '') {
  const types = contentType.replace(' ', '').split(';')
  return {
    type: types[0],
    charset: types[1]?.match(/charset=(.*)/)?.[1],
  }
}

function joinUrl(url = '', pathname = '', isHttp?: boolean) {
  url = typeof isHttp === "boolean"
    ? isHttp ? url.replace('https://', 'http://') : url.replace('http://', 'https://')
    : url
  const u = new URL(url)
  return pathname.startsWith('/') ? path.join(u.origin, pathname) : path.join(url, pathname)
}
